package com.jphenix.driver.regc;

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletResponse;

import com.jphenix.driver.cluster.ClusterFilter;
import com.jphenix.driver.cluster.IClusterTrigger;
import com.jphenix.driver.cluster.ServerInfoVO;
import com.jphenix.driver.json.Json;
import com.jphenix.driver.regc.vo.ContextVO;
import com.jphenix.driver.threadpool.ThreadSession;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanFactoryManager;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanRegister;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanRegisterChild;
import com.jphenix.kernel.script.ScriptLoader;
import com.jphenix.kernel.script.ScriptVO;
import com.jphenix.kernel.script.ScriptWrapper;
import com.jphenix.driver.regc.vo.ActionVO;
import com.jphenix.servlet.common.ActionContext;
import com.jphenix.servlet.common.HttpServletRequestImpl;
import com.jphenix.servlet.filter.FilterExplorer;
import com.jphenix.servlet.parent.ServiceBeanParent;
import com.jphenix.share.lang.SDate;
import com.jphenix.share.printstream.PrintWriterTool;
import com.jphenix.share.util.BaseUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.docs.BeanInfo;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.docs.Running;
import com.jphenix.standard.exceptions.FixException;
import com.jphenix.standard.script.IScriptBean;
import com.jphenix.standard.servlet.IActionBean;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.servlet.IFilter;
import com.jphenix.standard.servlet.IRequest;
import com.jphenix.standard.servlet.IResponse;
import com.jphenix.standard.viewhandler.IViewHandler;

/**
 * 注册中心过滤器
 * com.jphenix.driver.regc.RegistCenterFilter
 * 
 * 规则：
 * 1. 本地注册的动作类，没有虚拟路径(只有本地注册动作类的虚拟路径为空)
 * 2. 本地注册全部完毕后，或者发生变化时，只将本地注册的信息推送给注册中心数据主服务器，并不做广播。由注册中心主服务器统一广播
 * 
 * 2021-04-27 去掉了不匹配动作检测容器，因为会出现开始不存在该动作，随后就存在了。如果放在不匹配容器中，随后一直不匹配
 * 
 * @author MBG
 * 2021年03月10日
 */
@Running({"90"})
@ClassInfo({"2021-04-27 15:59","注册中心过滤器"})
@BeanInfo({"regc"})
public class RegistCenterFilter extends ServiceBeanParent implements IFilter,IClusterTrigger,IBeanRegister {

  private   String         actionName         = null;              // 响应内部指令处理动作名 (从配置文件中获取值)
  private   String         localActionExtName = null;              // 本地动作扩展名
  private   boolean        enabledBeanReg     = true;              // 是否允许本地动作类直接注册使用 (从配置文件中获取值)
	protected FilterExplorer fe                 = null;              // 过滤器管理类
  protected FilterConfig   config             = null;              // 过滤器配置信息处理类
  private   boolean        disabledCluster    = true;              // 是否禁用集群
  private   ClusterFilter  cluster            = null;              // 集群管理类
  //private   String         sessionCookieName  = "JSESSIONID";    // 在Cookie中保存的会话信息主键名
  private   String         loopServerId       = null;              // 回路服务器ID
  //private   String         callLoopServerId   = null;            // 访问回路服务器需要经过的中间服务器主键
  //private   int            loopServerLevel    = -1;              // 回路中转层级
  private   String         localContextPath   = null;              // 本地虚拟巨鲸（从配置文件中获取）
  private   String[] allowOriginVals          = null;              // 允许跨站的域名数组，*为全部域名
  private   ScriptLoader   sl                 = null;              // 脚本加载器
  private   Map<String,ContextVO> ctxMap      = new HashMap<>();   // 动作对照容器
  private   ContextVO localContextVO          = null;              // 本地上下文信息容器
  //private   Map<String,String> noMatchingMap  = new HashMap<>();   //不匹配的URL容器 废弃该功能，因为会存在这种情况，之前没这个动作，随后就有了，结果被这个容器给屏蔽掉了

  /**
   * 【事件】由ClusterFilter调用，集群服务加载完毕后调用该方法
   * 2021年03月10日
   */
  @Override
  public void clusterInited(ClusterFilter caller) {
		cluster = caller;
		if(!cluster.disabled()) {
			disabledCluster = false;
		}
		//本机配置信息
		ServerInfoVO localVO = cluster.getLocalServerInfoVO();
		//在Cookie中保存的会话信息主键名
		String value = localVO.parameter("session_cookie_name");
		if(value.length()>0) {
			//sessionCookieName = value;
		}
  }

  /**
	 * 【事件】由ClusterFilter调用，首次完成发送心跳到某个目标服务器后执行该方法
	 * @param siVO   目标服务器信息类
	 * 2021年03月10日
	 * @author MBG
	 */
  @Override
  public void validSended(ServerInfoVO siVO) {
    //TODO 待完善
		//返回报文头
		Map<String,String> header = new HashMap<String,String>();
		header.put("REG_MSG_TYPE","1");              //内部指令 获取配置信息
		header.put("REG_SRC_CLIENT",loopServerId);   //标记源头服务器
		header.put("REG_SENDER",cluster.name());     //标记调用者服务器
		header.put("REG_LEVEL","1");    
		header.put("REG_OBJ_CLIENT",siVO.name); //设置目标服务器主键
		try {
			//获取配置信息
			IViewHandler res = cluster.postRvh(siVO,actionName,null,0,header);
			if(res==null || res.a("sn").length()<1) {
				//这个目标系统也没有获取到配置信息
				return;
			}
      /*
			if(boo(header.get("IFR_RECIVE_STATUS")) && res!=null && !res.isEmpty()) {
				setConfigure(res,null); //设置配置信息
				configLoaded = true;
			}
      */
		}catch(Exception e) {}
  }

	/**
	 * 【事件】由ClusterFilter调用，首次完成接收目标心跳后执行该方法 （无用）
	 * @param siVO   发送方服务器信息类
	 * 2021年03月10日
	 * @author MBG
	 * @deprecated 无用
	 */
  @Override
  public void validReceived(ServerInfoVO siVO) {}

	/**
	 * 返回触发响应主键
	 * @return 触发响应主键
	 * 2021年03月10日
	 * @author MBG
	 */
  @Override
  public String triggerKey() {
    return "_reg_";
  }

	/**
	 * 响应发起服务器请求
	 * @param siVO   发起服务器信息类
	 * @param req    请求对象
	 * @param resp   反馈对象
	 * 2019年10月8日
	 * @author MBG
	 */
  @Override
  public void triggerRecive(ServerInfoVO siVO, IRequest req, IResponse resp) {}

    /**
     * 获取排序索引，数值越小越先执行
     * @author MBG
     * 2021-03-10 16:41
     * @return 排序索引
     */
  @Override
  public int getIndex() {
    //注意：该值一定要比ConsoleFilter中的索引值大，否则会不认加密后的动作路径
    return 60;
  }

    /**
     * 获取需要过滤的动作路径扩展名
     * @return 扩展名
     * 2021-03-10 16:41
     * @author MBG
     */
  @Override
  public String getFilterActionExtName() {
    return "*";
  }

  /**
   * 执行过滤
   * 刘虻
   * 2021-03-10 16:41
   * @param req        页面请求
   * @param resp       页面反馈
   * @return           如果返回真，则不继续往下进行，直接跳过结束
   * @throws Exception 执行发生异常
   */
  @Override
  public boolean doFilter(IRequest req, IResponse resp) throws Exception {
		//获取动作路径，不带虚拟路径
		String servletPath = req.getServletPath();
    if(servletPath.equals(actionName)) {
      //TODO 内部请求
      return true;
    }
		if(cluster!=null && 
        !cluster.disabled() && 
        cluster.callFromCluster(req) && 
        str(req.getHeader("_cluster_call_")).length()>0) {
			//在线开发，代理入口
			return false;
		}
		String subPath    = null; //触发相对路径
		String subServlet = null; //除去一级路径的后续路径
		int point         = servletPath.indexOf("/",1); //获取一级路径作为触发相对路径
		if(point>0) {
			subPath     = servletPath.substring(0,point);
			subServlet  = servletPath.substring(point);
		}else {
      subPath     = "";
			subServlet  = servletPath;
		}
    ActionVO avo; //动作信息类
    long     now; //当前时间
    if(localContextVO!=null && subPath.equals(localContextPath)){
      //本地请求
      avo = localContextVO.actionMap.get(subServlet);
      if(avo==null){
        return false;
      }
      now = System.currentTimeMillis(); //当前时间
      recordAction(avo,now); //在调用动作前，记录相关请求信息
      try{
        callLocal(avo,req,resp); //调用本地动作
      }catch(Throwable e){
        e.printStackTrace();
        recordActionError(avo,e); //记录出错信息
      }finally{
        recordActionFinall(avo,now); //记录动作结束时的信息
      }
      return true;
    }
    //获取远程上下文信息类
    ContextVO rctxVO = ctxMap.get(subPath);
    if(rctxVO==null){
      return false;
    }
    if(rctxVO.needProxy){
      //TODO  代理全部请求

      return true;
    }
    avo = rctxVO.actionMap.get(subServlet);
    if(avo!=null){
      return true;
    }
    now = System.currentTimeMillis(); //当前时间
    recordAction(avo,now); //在调用动作前，记录相关请求信息
    try{
      callRemote(avo,req,resp); //远程调用动作
    }catch(Throwable e){
      e.printStackTrace();
      recordActionError(avo,e); //记录出错信息
    }finally{
      recordActionFinall(avo,now); //记录动作结束时的信息
    }
    return false;
  }

  /**
   * 记录动作结束时的信息
   * @param avo 动作信息类
   * @param now 开始时间
   */
  private void recordActionFinall(ActionVO avo,long now){
    avo.lastInvokeUseTime = System.currentTimeMillis()-now;
  }

  /**
   * 记录动作出错信息
   * @param avo 动作信息类
   * @param e   异常信息类
   */
  private void recordActionError(ActionVO avo,Throwable e){
    if(avo.errorCount>Long.MAX_VALUE){
      avo.errorCount = 1;
    }else{
      avo.errorCount++;
    }
    if(avo.errorDayCount>Long.MAX_VALUE){
      avo.errorDayCount = 1;
    }else{
      avo.errorDayCount++;
    }
    avo.lastErrorTime = System.currentTimeMillis();
    avo.lastErrorMsg = e.toString();
  }

  /**
   * 在调用动作前，记录相关请求信息
   * @param avo 动作信息类
   * @param now 当前时间
   */
  private void recordAction(ActionVO avo,long now){
    if(avo.invokeCount>Long.MAX_VALUE){
      avo.invokeCount = 1;
    }else{
      avo.invokeCount++;
    }
    if(avo.dayEndTime<now){
      //新的一天，设置当日结束时间
      avo.dayEndTime = (new SDate(SDate.nowDateString()+" 23:59:59 999")).getMillisecond();
      avo.invokeDayCount = 1;
      avo.errorDayCount  = 0;
    }else if(avo.invokeDayCount>Long.MAX_VALUE){
      avo.invokeDayCount = 1;
    }else{
      avo.invokeDayCount++;
    }
  }

	/**
	 * 执行初始化
	 * @param fe         过滤器管理类
	 * @param config     Servlet配置信息类
	 * @throws Exception 异常（如果初始化发生异常，则放弃不再使用）
	 * 2021-03-10 16:41
	 * @author MBG
	 */
  @Override
  @SuppressWarnings("deprecation")
  public void init(FilterExplorer fe, FilterConfig config) throws Exception {
		this.fe     = fe;
		this.config = config;
    this.sl     = (ScriptLoader)((IBeanFactoryManager)_beanFactory).getNativeObject(ScriptLoader.class,this);
    if(localContextPath==null){
      localContextPath = "";
    }
  }

  /**
   * 调用本地动作
   * @param avo   动作信息类
   * @param req   请求对象
   * @param resp  返回对象
   * @throws Exception 异常
   */
  private void callLocal(ActionVO avo,IRequest req,IResponse resp) throws Exception {
    if(avo.scriptId==null && avo.scriptId.length()<1){
      resp.sendError(HttpServletResponse.SC_NOT_FOUND,
        "The Action ["+avo.actionPath+"] Not Found The ScriptID");
      return;
    }
    //动作上下文
    IActionContext ac = new ActionContext(
      req,resp,req.getServerName().toLowerCase()
      ,avo.actionPath,uploadPath());
    //清除缓存
    ac.getResponse().setHeader("Pragma","No-cache");
    ac.getResponse().setHeader("Cache-Control","no-cache");
    ac.getResponse().setDateHeader("Expires", 0);
    //解决跨域访问session丢失问题
    ac.getResponse().setHeader("P3P","CP=CAO PSA OUR");
    String[] vals = getAllowOriginVals(); //获取允许跨站域名数组
    for(int i=0;i<vals.length;i++) {
      ac.getResponse().setHeader("Access-Control-Allow-Origin",vals[i]); 
    }
    //获取线程会话
    ThreadSession.put("_action_context",ac);            //放入动作上下文
    ThreadSession.put("_session_id",ac.getSessionID()); //将会话主键也放入线程会话
    ThreadSession.put("_action_ip",ac.cip());           //将请求IP地址放入线程会话
    ThreadSession.put("_action_name",avo.actionPath);              //将动作路径放入线程会话
    invokeScriptAction(avo,ac); //调用指定脚本
  }

  /**
   * 调用本地动作脚本
   * @param avo 动作信息类
   * @param ac  动作上下文
   * @throws Exception 异常
   */
  public void invokeScriptAction(ActionVO avo,IActionContext ac) throws Exception {
    Map<String,String> initParaMap = null; //初始参数容器
    if(avo.sParameter!=null && avo.sParameter.length()>0){
      initParaMap = StringUtil.fixQueryString(avo.sParameter);
    }
    invokeScriptAction(avo.actionPath,avo.scriptId,ac,initParaMap);
  }

  /**
   * 获取动作类（获取过程异常信息直接输出到界面，不用再做处理）
   * @param actionPath  动作路径（用于输出日志）
   * @param scriptId    脚本主键
   * @param ac          动作上下文
   * @param extParas    扩展参数 0是否输出日志（该值需要返回传出）
   * @return            脚本动作类实例
   * @throws Exception  异常
   */
  public IActionBean getActionScript(
    String actionPath,
    String scriptId,
    IActionContext ac,
    Object[] extParas) throws Exception {
    //获取对应的脚本信息类
    ScriptVO sVO = sl.getScriptInfo(scriptId,false);
    if(sVO==null) {
      ac.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND,
      "Not Find The Script:["+scriptId+"] By Action:["+actionPath+"]");
      warning("没有获取到脚本动作类:["+scriptId+"]");
      return null;
    }
    if(sVO.clusterCallOnly) {
      if(cluster!=null && !cluster.allow(ac.getRequest())) {
        ac.getResponse().sendError(HttpServletResponse.SC_FORBIDDEN,"This Action:["+
          actionPath+"] Only cluster server calls are allowed",ac.getRequest());
        warning("收到集群外的请求:["+actionPath+"]，已经被禁止  客户端IP:["+ac.getRequest().cip()+"]");
        return null;
      }
    }
    if(sVO.actionSpecialCode) {
      //设置允许提交特殊代码
      if (ac.getRequest() instanceof HttpServletRequestImpl) {
          ac.getRequest().setDisabledWrapper(true);
      }
    }
    //获取脚本类
    Object sObj = sl.getScript(sVO);
    if(sObj==null) {
        warning("遇到意外，获取脚本:["+scriptId+"]类实例为空");
        ac.getResponse().sendError(HttpServletResponse.SC_NOT_FOUND,"Not Find The Script:["+
          scriptId+"] Instance By Action:["+actionPath+"]",ac.getRequest());
        return null;
    }
    if(sObj instanceof IActionBean 
      || (sObj instanceof ScriptWrapper 
          && ((ScriptWrapper)sObj).implementsClass(IActionBean.class))) {
      if(outLog() && sVO.noOutLog) {
        if(extParas!=null && extParas.length>0){
          extParas[0] = true;
        }
        outLog(false);
      }
      ((IActionBean)sObj).setActionContext(ac);
      //常规动作脚本
      log("LoadScript:["+sVO.id+"] ["+sVO.title+"] Source:["+ac.getRequest().getRemoteAddr()+"]");
      ThreadSession.put("_action_ invoker_",sVO.id); //将调用者名字放入线程会话中
    }else{
      warning("遇到意外，该脚本:["+scriptId+"]类实例并非动作类实例，动作路径：["+actionPath+"]");
      return null;
    }
    return (IActionBean)sObj;
  }

  /**
   * 执行动作脚本
   * @param action      动作路径
   * @param ab          脚本动作类实例
   * @param ac          动作上下文
   * @param initParaMap 初始参数容器
   * @throws Exception  异常
   */
  public void executeAction(
    String         action,
    IActionBean    ab,
    IActionContext ac,
    Map<String,?>  initParaMap) throws Exception {
    if(ac.isPost() && !((IScriptBean)ab).isEnabledActionCharge()) {
      //是否允许动作类同一个用户并行执行
      //使用这个功能的具体原因请看ScriptVO类中，该变量的注释
      if(scriptActionChargeCheck(action,ac,false)) {
        ac.getResponse().sendError(423,"Disables the user from repeating the current action",ac.getRequest());
        return;
      }
    }
    try{
      //整理传入参数 这么做主要是能让beforeRunAction方法获取到即将传入的参数
      //有些参数只能获取一次，比如XML对象或Json对象，所以提前先取好，然后用在多个方法中
      ab.buildActionParameter(initParaMap);
      if(!ab.beforeRunAction()) {
        return;
      }
      try{
        //调用动作
        ab.invokeAction();
      }catch(FixException e){
        //已经做好处理，无需再抛异常出去
      }
      ab.afterRunAction();
    }finally{
      scriptActionChargeCheck(action,ac,true); //释放锁定
    }
  }

  /**
   * 1.在指定这个工作之前判断当前用户：是否已经正在执行了这个动作
   * 2.会写当前动作执行完毕
   * @param action  动作路径
   * @param ac      动作上下文
   * @param setFree 是否回写当前动作是否执行完毕
   * @return true 正在执行这个动作（当前用户重复执行指定动作）
   * 2016年12月23日
   * @author MBG
   */
  @SuppressWarnings("unchecked")
	protected boolean scriptActionChargeCheck(String action,IActionContext ac,boolean setFree) {
    //从会话中提取记录容器
    Map<String,Boolean> actionChargeMap = (Map<String,Boolean>)ac.getSessionAttribute("_action_charge_info_");
    if(actionChargeMap==null) {
      actionChargeMap = new HashMap<String,Boolean>();
      ac.setSessionAttribute("_action_charge_info_",actionChargeMap);
    }
    synchronized(actionChargeMap) {
      if(setFree) {
        actionChargeMap.put(action,false);
        return false;
      }
      if(actionChargeMap.containsKey(action) && actionChargeMap.get(action)) {
        return true;
      }
      actionChargeMap.put(action,true);
      return false;
    }
  }

  /**
   * 远程调用该动作
   * @param avo   动作信息类
   * @param req   请求对象
   * @param resp  返回对象
   */
  private void callRemote(ActionVO avo,IRequest req,IResponse resp){
    //FIXME
  }

  /**
   * 通过请求url（包含一级虚拟路径）返回动作信息类
   * @param actionPath 请求url（包含一级虚拟路径）
   * @return 对应的动作信息类
   */
  public ActionVO getActionVO(String actionPath){
    if(actionPath==null){
      return null;
    }
    //分隔符位置
    int point = actionPath.indexOf("?");
    if(point>0){
      actionPath = actionPath.substring(0,point);
    }
    if(!actionPath.startsWith("/")){
      actionPath = "/"+actionPath;
    }
    point = actionPath.indexOf("/",1);
    if(point<0){
      return getActionVO(null,actionPath);
    }
    return getActionVO(actionPath.substring(0,point),actionPath.substring(point));
  }


  /**
   * 获取对应的动作信息类
   * @param contextPath 一级虚拟路径
   * @param actionPath  除去一级虚拟路径的动作路径
   * @return 对应的动作信息类
   */
  public ActionVO getActionVO(String contextPath,String actionPath){
    //通过虚拟路径
    ContextVO ctxVO = ctxMap.get(contextPath);
    if(ctxVO==null){
      return null;
    }
    return ctxVO.actionMap.get(actionPath);
  }

  /**
   * 处理 Access-Control-Allow-Origin
   * @return 允许跨站域名
   * 2019年6月28日
   * @author MBG
   */
  private String[] getAllowOriginVals() {
    if(allowOriginVals==null) {
      //获取允许跨站域名信息
      String val = p("servlet_access_control_allow_origin");
      if(val.length()<1) {
        allowOriginVals = new String[0];
      }else {
        allowOriginVals = BaseUtil.split(val,",",";","，","；");
      }
    }
    return allowOriginVals;
  }

  /**
   * 执行脚本动作
   * @param scriptId       脚本主键
   * @param req            动作请求
   * @param resp           执行反馈
   * @throws Exception     异常
   * 2019年12月10日
   * @author MBG
   */
  public void invokeScriptAction(
    String scriptId,
    IRequest req,
    IResponse resp) throws Exception {
    invokeScriptAction(scriptId,req,resp,null,null);
  }

  /**
   * 执行脚本动作
   * @param scriptId       脚本主键
   * @param req            动作请求
   * @param resp           执行反馈
   * @param domain         当前访问过来的域名
   * @param action         动作名
   * @param uploadBasePath 文件上传根路径
   * @throws Exception     异常
   * 2019年12月4日
   * @author MBG
   */
  public void invokeScriptAction(
    String    scriptId,
    IRequest  req,
    IResponse resp,
    String    domain,
    String    action) throws Exception {
    invokeScriptAction(scriptId,scriptId,new ActionContext(req,resp,domain,action,uploadPath()),null);
  }

  /**
   * 执行脚本动作
   * @param action      动作路径
   * @param scriptId    脚本主键
   * @param ac          动作上下文
   * @param initParaMap 初始参数容器
   * @throws Exception  异常
   */
  public void invokeScriptAction(
    String         action,
    String         scriptId,
    IActionContext ac,
    Map<String,?>  initParaMap) throws Exception {
    String queryStr = ac.getRequest().getQueryString(); //url请求参数
    boolean needOutLog = false; //执行后是否允许输出日志
    if(outLog() && queryStr!=null && queryStr.indexOf("_nolog_")>0){
      outLog(false);
      needOutLog = true;
    }
    info("Start Execute Local ActionScript:["+scriptId+"] Action:["+action+"]");
    //获取动作对应的方法名
    Object time = log.runBefore(); //标记执行开始时间
    try{
      Object[] extParas = new Object[1];
      extParas[0] = needOutLog;
      //强制转换为动作类
      IActionBean ab = getActionScript(action,scriptId,ac,extParas);
      if(ab==null){
        return;
      }
      needOutLog = (boolean)extParas[0];
      ab.setActionContext(ac); //设置动作上下文
      executeAction(action,ab,ac,initParaMap); //执行动作脚本
      if(ab.isNoReturnInfo()){
        return;
      }
      IViewHandler vh = null; //页面对象
      Json   reJson   = null; //即将输出的Json对象
      String encoding = null; //输出编码
      switch(ab.getOutContentType()) {
        case 0:
          vh = ab.beforeOutView(ab.getReVh());
          encoding = vh.getDealEncode();
          if(encoding==null || encoding.length()<1) {
            encoding = "UTF-8";
          }
          ac.getResponse().setHeader("Content-Type","text/html; charset="+encoding);
          break;
        case 1:
          vh = ab.beforeOutView(ab.getReXml());
          if(vh!=null) {
            encoding = vh.getDealEncode();
            if(encoding==null || encoding.length()<1) {
              encoding = "UTF-8";
            }
          }else {
            encoding = "UTF-8";
          }
          ac.getResponse().setHeader("Content-Type","text/xml; charset="+encoding);
          break;
        default:
            reJson = ab.beforeOutJson(ab.getReJson());
            ac.getResponse().setHeader("Content-Type","application/json; charset=UTF-8");
            break;
      }
      boolean noOut = true; //是否没输出数据
      if(reJson!=null && !reJson.isEmpty()) {
        /* jsonp 支持方法
         * 如果存在这个参数值，则采用jsonp的返回格式
         * 即：方法名(json返回内容);
         * 
         * 方法名是从request中，获取的  request.getParameter("jsoncallback");
         * 
         * jsoncallback 是写死的，目前还没有改变的必要
         * 
         */
        String jsonCallBack = ac.getParameter("jsoncallback");
        
        //输出流
        PrintWriter writer = ac.getResponse().getWriter();
        if(jsonCallBack.length()>0) {
            debug("===============Jsonp Mode:[YES]===============");
            debug(reJson.toString());
            debug("(=======Return=======)");
            writer.write(jsonCallBack+"(");
            writer.write(reJson.toString());
            writer.write(");");
        }else {
            debug(reJson.toString());
            debug("(=======Return=======)");
            writer.write(reJson.toString());
        }
        noOut = false;
      }
      if(noOut && vh!=null) {
        //之前遇到个无法呈现的问题，偶尔输出内容全是问号
        //原来使用的是vh.getDealEncode();这回写死UTF-8
        //基本输出编码 weblogic只能用基本编码输出
        String standardOutEncoding = "UTF-8";
        try {
          //设置编码格式
          ac.getResponse().setCharacterEncoding(standardOutEncoding);
        }catch(NoSuchMethodError e) {
          //使用WebLogic时会抛异常到这里，然后使用标准输出
          //非weblogic不能使用标准输出，否则反而出现乱码
          standardOutEncoding = "ISO-8859-1";
        }
        try {
            //输出信息到页面
            vh.getNodeBody(
              new PrintWriterTool(
                ac.getResponse().getWriter(),
                vh.getDealEncode(),
                standardOutEncoding,
                false));
            //不能输出html代码到页面，太乱
        }catch(Exception e) {}
      }
    }finally{
      log.writeRuntime(time,"Execute Complete Local ActionScript:["+scriptId+"] Action:["+action+"]");
      if(needOutLog){
        outLog(true);
      }
    }
  }

  /**
   * 覆盖方法
   */
  @Override
  public boolean regist(Object bean) {
    if(!enabledBeanReg){
      return true;
    }
    if(!(bean instanceof IActionBean)) {
      return false;
    }
    //脚本主键
    String scriptId = str(((IScriptBean)bean).getScriptId()).toUpperCase();
    //动作路径
    String action = "/"+scriptId+"."+localActionExtName;
    //获取该脚本对应的脚本信息类
    ScriptVO sVO = ((IScriptBean)bean).getScriptVO();
    //本地上下文信息类
    if(localContextVO==null){
      localContextVO = new ContextVO();
      localContextVO.localReg    = true;
      localContextVO.contextPath = localContextPath;
      ctxMap.put(localContextPath,localContextVO);
    }
    //获取该动作信息类
    ActionVO avo = localContextVO.actionMap.get(action);
    if(avo==null){
      avo = new ActionVO();
      avo.actionPath = action;
      localContextVO.actionMap.put(action,avo);
    }else{
      warning("重复注册动作类，动作路径:["+
        action+"] 已注册脚本：["+avo.scriptId+
        "] 随后注册脚本：["+scriptId+"]");
    }
    avo.contextPath   = "";
    avo.groupId       = "_local_";
    avo.title         = sVO.title;
    avo.scriptId      = scriptId;
    avo.isLocalServer = true;
    if(!disabledCluster && cluster!=null){
      avo.clusterGroupId  = cluster.group();
      avo.clusterServerId = cluster.name();
    }
    if(bean instanceof IBeanRegisterChild) {
      ((IBeanRegisterChild)bean).setRegister(this);
      ((IBeanRegisterChild)bean).afterRegister();
    }
    log.startLog("---Regc:["+sVO.id+" "+sVO.title+"] action:["+action+"]");
    return true;
  }

  /**
   * 覆盖方法
   */
  @Override
  public void unRegist(Object bean) {
    if(!enabledBeanReg){
      return;
    }
    if(!(bean instanceof IActionBean)) {
      return;
    }
    if(localContextVO==null){
      return;
    }
    if(bean instanceof IBeanRegisterChild) {
      ((IBeanRegisterChild)bean).beforeUnRegister();
    }
    //移除动作信息
    localContextVO.actionMap.remove("/"+str(((IScriptBean)bean).getScriptId()).toUpperCase()+"."+localActionExtName);
  }
}
